package com.purang.web.schedule.quartz.core;

import com.purang.web.config.HttpClientUtilsConfig;
import com.purang.web.schedule.quartz.job.QuartzJob;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.Assert;

import java.io.File;
import java.net.URL;
import java.net.URLDecoder;

/**
 * spring-boot自动注册quartz定时任务
 *
 * @author Zheming Liu
 * @version 1.0-RELEASE
 * @since 1.0-RELEASE
 */
@Slf4j
@Configuration
@ConditionalOnClass(Scheduler.class)
@ConditionalOnBean(HttpClientUtilsConfig.class)
public class QuartzRegisterListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private QuartzService quartzService;

    /**
     * job所在包路径
     */
    @Value("${quartz.job-package}")
    private String jobDirPackage;

    /**
     * 自动发布quartz job
     */
    @Override
    @SneakyThrows
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //获得当前线程的类加载器
        log.info("Quartz schedule start,job package is {}", jobDirPackage);
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String relativeJobPackagePath = this.jobDirPackage.replace(".", "/");
        //获取对应包的URL
        URL jobPackageUrl = Thread.currentThread().getContextClassLoader().getResource(relativeJobPackagePath);
        //获取包的绝对路径
        String absolutelyJobPackagePath = URLDecoder.decode(jobPackageUrl.getFile(), "UTF-8");
        //获得包的File
        File jobDir = new File(absolutelyJobPackagePath);
        //包扫描
        scanPackage(jobDir, classLoader, jobDirPackage);
    }

    /**
     * 包扫描，递归加载对应的Class
     *
     * @param jobDir      包文件
     * @param classLoader 类加载器
     * @param packagePath 包路径
     * @throws ClassNotFoundException 类加载异常
     * @throws SchedulerException     定时任务创建异常
     */
    private void scanPackage(File jobDir, ClassLoader classLoader, String packagePath) throws ClassNotFoundException, SchedulerException {
        Assert.notNull(jobDir, "Parameter jobDir can't be null");
        Assert.notNull(classLoader, "Parameter classLoader can't be null");
        Assert.notNull(packagePath, "Parameter packagePath can't be null");
        if (jobDir.isDirectory()) { //目标是文件夹
            for (File jobFile : jobDir.listFiles()) {
                String packageNameDir = packagePath;
                if (jobFile.isDirectory()) {
                    packageNameDir = packagePath + "." + jobFile.getName();
                }
                scanPackage(jobFile, classLoader, packageNameDir);
            }
        } else { //目标是文件
            if (jobDir.getName().endsWith(".class")) {
                String packageNameFile = packagePath + "." + jobDir.getName().replace(".class", "");
                Class<?> jobClass = classLoader.loadClass(packageNameFile);
                log.info("ClassLoader load class {}", jobClass.getName());
                QuartzRegister quartzRegister = jobClass.getAnnotation(QuartzRegister.class);
                //注册quartz job
                if (quartzRegister != null) {
                    Assert.isTrue(QuartzJob.class.isAssignableFrom(jobClass) && jobClass != QuartzJob.class, "Annotation @QuartzRegister can't on " + jobClass.getName());
                    quartzService.createScheduleJob(quartzRegister.jobDetailName(), quartzRegister.cronExpression(), (Class<? extends Job>) jobClass);
                    log.info("Quartz register job {}", jobClass.getName());
                }
            }
        }
    }
}
